﻿Imports System
Imports System.Collections.Generic
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Data
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Media.Imaging
Imports System.Windows.Navigation
Imports System.Windows.Shapes

Partial Public Class ControlAction

    Public Sub New()
        MyBase.New()
        Me.InitializeComponent()

        Me.__CurrentExpressionTextbox.Text = String.Empty
        Me.__CurrentExpressionProcessButton.ToolTip = "Compile and solve" + vbCrLf + "shortcut: enter"

        __PopulateUsedExpressionsArea()
        __ClearCurrentExpressionArea()

        AddHandler Application.Compiler.ExpressionVariablesTypesRequest, AddressOf __Compiler_ExpressionVariablesTypesRequest
    End Sub

    Private Sub __PopulateUsedExpressionsArea()
        For _counter As Integer = 0 To UsedExpressionCollection.Count - 1
            Dim _usedExpression As UsedExpression = UsedExpressionCollection.Item(_counter)
            __AddUsedExpressionButton(_usedExpression)
        Next _counter
    End Sub

    Private Sub __AddUsedExpressionButton(ByVal _usedExpression As UsedExpression)
        Dim _button As Button = New Button()
        _button.DataContext = _usedExpression
        _button.Style = CType(FindResource("UsedExpressionStyle"), Windows.Style)

        Dim _buttonTooltip As String = String.Empty
        _buttonTooltip += "Expression: " + _usedExpression.Text + vbCrLf
        If _usedExpression.Variables.Count >= 1 Then
            _buttonTooltip += "Variables:" + vbCrLf
            For Each _one As UsedExpressionVariable In _usedExpression.Variables
                If _one.Value Is Nothing Then
                    _buttonTooltip += "    " + _one.Name + " (" + _one.Type + "): " + "#null#" + vbCrLf
                Else
                    _buttonTooltip += "    " + _one.Name + " (" + _one.Type + "): " + _one.Value.ToString() + vbCrLf
                End If
            Next _one
        End If
        _buttonTooltip += vbCrLf
        If _usedExpression.ResultValue IsNot Nothing Then
            _buttonTooltip += "Result: " + _usedExpression.ResultValue.ToString()
        Else
            If _usedExpression.ResultExceptionType IsNot Nothing Then
                _buttonTooltip += "Resulting error: " + _usedExpression.ResultExceptionType
            End If
        End If
        _button.ToolTip = _buttonTooltip

        Me.__UsedExpressionsArea.Children.Add(_button)
        Me.__UsedExpressionsAreaScroller.ScrollToBottom()

        AddHandler _button.Click, AddressOf __UsedExpressionButton_Click
    End Sub

    <PreEmptive.Attributes.Feature("ExpressionReused")> _
    Private Sub __UsedExpressionButton_Click(ByVal _sender As Object, ByVal _data As RoutedEventArgs)
        Me.__CurrentExpressionTextbox.Text = CType(CType(_sender, Button).DataContext, UsedExpression).Text
        Me.__CurrentExpressionTextbox.Focus()
    End Sub

    <PreEmptive.Attributes.Feature("ProcessExpressionByClick")> _
    Private Sub __CurrentExpressionProcessButton_MouseLeftButtonUp(ByVal _sender As Object, ByVal _data As System.Windows.Input.MouseButtonEventArgs) Handles __CurrentExpressionProcessButton.MouseLeftButtonUp
        __CompileAndSolveCurrentExpression()
    End Sub

    <PreEmptive.Attributes.Feature("ProcessExpressionByKeyboard")> _
    Private Sub __CurrentExpressionTextbox_KeyUp(ByVal _sender As Object, ByVal _arguments As System.Windows.Input.KeyEventArgs) Handles __CurrentExpressionTextbox.KeyUp
        If _arguments.Key = Key.Enter Or _arguments.Key = Key.Return Then
            _arguments.Handled = True
            __CompileAndSolveCurrentExpression()
        End If
    End Sub

    Private Sub __CurrentExpressionTextbox_TextChanged(ByVal _sender As Object, ByVal _data As System.Windows.Controls.TextChangedEventArgs) Handles __CurrentExpressionTextbox.TextChanged
        __ClearCurrentExpressionArea()
    End Sub

    Private Sub __ClearCurrentExpressionArea()
        __CurrentExpressionResultValueArea.Visibility = Windows.Visibility.Collapsed
        __CurrentExpressionResultExceptionArea.Visibility = Windows.Visibility.Collapsed
        __CurrentExpressionExecutionArea.Visibility = Windows.Visibility.Collapsed
        __CurrentExpressionExecutionTimeLabel.Visibility = Windows.Visibility.Collapsed
    End Sub

    Private Function __DetermineExceptionMessage(ByVal _exception As System.Exception) As String
        Dim _returnValue As String = String.Empty

        Select Case _exception.GetType().Name
            Case "IsNullException", "CompilerExpressionIsEmptyException"
                _returnValue = "Can not be empty"
            Case "CompilerExpressionStringsDelimitersCountIsValidNotException"
                _returnValue = "Strings delimiters count is invalid"
            Case "CompilerExpressionStringsAreValidNotException"
                _returnValue = "At least one string is invalid"
            Case "CompilerExpressionDateTimesDelimitersCountIsValidNotException"
                _returnValue = "DateTime's delimiters count is invalid"
            Case "CompilerExpressionDateTimesAreValidNotException"
                _returnValue = "At least one dateTime is invalid"
            Case "CompilerExpressionLevelsDelimitersCountAreEqualNotException"
                _returnValue = "Parenthesis count is invalid"
            Case "CompilerExpressionLevelsDelimitersAreValidNotException"
                _returnValue = "Levels nesting is invalid"
            Case "CompilerExpressionHasUndeterminedTokensException"
                _returnValue = "Some parts of the expression are incorrect"
            Case "CompilerExpressionHasWhiteSpaceTokensOnlyException"
                _returnValue = "Expression only contains white-space"
            Case "CompilerExpressionVariablesTypesRequestAbortedException"
                _returnValue = "The variables types enquiry generated an error"
            Case "CompilerExpressionVariableTypeIsValidNotException"
                _returnValue = "Invalid usage of variables types"
            Case "CompilerExpressionSubexpressionStartsWithFunctionParametersSeparatorException"
                _returnValue = "Sub-expression starts with a parameters separator"
            Case "CompilerExpressionSubexpressionEndsWithFunctionParametersSeparatorException"
                _returnValue = "Sub-expression ends with a parameters separator"
            Case "CompilerExpressionSubexpressionContainsAdjacentFunctionParametersSeparatorsException"
                _returnValue = "Adjacent parameters separators found"
            Case "CompilerExpressionSubexpressionTokenTypeIsValidNotException"
                _returnValue = "Invalid tokens found"
            Case "CompilerExpressionSubexpressionContainsAdjacentValuesOrVariablesOrFunctionCallsException"
                _returnValue = "Sub-expression contains adjacent values or variables or function calls"
            Case "CompilerExpressionSubexpressionContainsAdjacentOperatorsException"
                _returnValue = "Adjacent operators found"
            Case "CompilerExpressionSubexpressionEndsWithOperatorException"
                _returnValue = "Sub-expression ends with an operator"
            Case "CompilerExpressionSubexpressionOperatorHasNoCorrespondingFunctionSignatureException"
                _returnValue = "No suitable function signature found"
            Case "CompilerExpressionLevelWhenEmptyMustBePrecededByFunctionNameException"
                _returnValue = "An empty sub-expression must be preceded by a function name"
            Case "CompilerExpressionFunctionNameAndParametersHaveNoCorrespondingFunctionSignatureException"
                _returnValue = "No suitable function signature found"
            Case "CompilerExpressionLevelWhenWithTwoOrMoreParametersMustBePrecededByFunctionNameException"
                _returnValue = "A sub-expression with two or more parameters must be preceded by a function name"
            Case "CompilerExpressionFunctionParametersAreOnlyAllowedInsideLevelsException"
                _returnValue = "Function parameters specification must occur in between parenthesis"
            Case "CompilerExpressionProducedNoEndResultException"
                _returnValue = "Expression produced no result"
            Case "CompilerExpressionRootTokenTypeIsValidNotException"
                _returnValue = "Invalid compilation result tree"
            Case Else
                _returnValue = _exception.GetType().Name
        End Select

        Return _returnValue
    End Function

    Private Sub __Compiler_ExpressionVariablesTypesRequest(ByVal _sender As Object, ByVal _data As Enhancements.Basic.Expressions.CompilerExpressionVariablesTypesRequestEventArgs)
        __Time_CompileVariablesTypesRequestStart = DateTime.Now
        WindowVariablesTypesRequest.Fill(_data)
        __Time_CompileVariablesTypesRequestStop = DateTime.Now
    End Sub

    Private __Time_CompileStart As DateTime? = Nothing
    Private __Time_CompileVariablesTypesRequestStart As DateTime? = Nothing
    Private __Time_CompileVariablesTypesRequestStop As DateTime? = Nothing
    Private __Time_CompileStop As DateTime? = Nothing
    Private __Time_SolveStart As DateTime? = Nothing
    Private __Time_SolveStop As DateTime? = Nothing

    Private Sub __CompileAndSolveCurrentExpression()
        __ClearCurrentExpressionArea()

        Dim _expression As Enhancements.Basic.Expressions.Expression = Nothing
        Dim _expressionResultValue As Object = Nothing
        Dim _expressionResultException As Exception = Nothing

        __Time_CompileStart = Nothing
        __Time_CompileVariablesTypesRequestStart = Nothing
        __Time_CompileVariablesTypesRequestStop = Nothing
        __Time_CompileStop = Nothing
        __Time_SolveStart = Nothing
        __Time_SolveStop = Nothing

        Try
            __Time_CompileStart = DateTime.Now
            _expression = Application.Compiler.Compile(Me.__CurrentExpressionTextbox.Text)
            __Time_CompileStop = DateTime.Now

            If _expression.Variables.Length >= 1 Then
                WindowVariablesValuesRequest.Fill(_expression)
            End If

            __Time_SolveStart = DateTime.Now
            _expressionResultValue = _expression.Solve()
            __Time_SolveStop = DateTime.Now

            __CurrentExpressionResultValueArea.Visibility = Windows.Visibility.Visible
            __CurrentExpressionResultValueLabel.Content = _expressionResultValue.ToString()

            Dim _executionTime As TimeSpan? = __DetermineExecutionTime()
            If _executionTime IsNot Nothing AndAlso _executionTime.HasValue Then
                __CurrentExpressionExecutionTimeLabel.Visibility = Windows.Visibility.Visible
                __CurrentExpressionExecutionTimeLabel.Content = CType(_executionTime.Value.TotalMilliseconds, Long).ToString() + " ms"
            End If

        Catch _exception As Exception
            __CurrentExpressionResultExceptionArea.Visibility = Windows.Visibility.Visible
            __CurrentExpressionResultExceptionLabel.Content = __DetermineExceptionMessage(_exception)
            _expressionResultException = _exception
        End Try

        If _expression IsNot Nothing Then
            __CurrentExpressionExecutionArea.Visibility = Windows.Visibility.Visible
            Dim _expressionExecution As String = __DetermineExecution(_expression)
            __CurrentExpressionExecutionLabel.Content = _expressionExecution
            __CurrentExpressionExecutionLabel.ToolTip = _expressionExecution

            UsedExpressionCollection.Append(_expression, _expressionResultValue, _expressionResultException)
        Else
            UsedExpressionCollection.Append(Me.__CurrentExpressionTextbox.Text, _expressionResultValue, _expressionResultException)
        End If
        __AddUsedExpressionButton(UsedExpressionCollection.Item(UsedExpressionCollection.Count - 1))
    End Sub

    Private Function __DetermineExecution(ByVal _expression As Enhancements.Basic.Expressions.Expression) As String
        Return __DetermineExecutionPart(_expression.RootToken)
    End Function

    Private Function __DetermineExecutionPart(ByVal _token As Enhancements.Basic.Expressions.Token) As String
        Dim _returnValue As String = String.Empty

        If TypeOf _token Is Enhancements.Basic.Expressions.Values.Value Then
            _returnValue = _token.ToRepresentation()
        Else
            If TypeOf _token Is Enhancements.Basic.Expressions.Variable Then
                _returnValue = _token.ToRepresentation()
            Else
                If TypeOf _token Is Enhancements.Basic.Expressions.Functions.FunctionCall Then
                    Dim _functionCall As Enhancements.Basic.Expressions.Functions.FunctionCall = CType(_token, Enhancements.Basic.Expressions.Functions.FunctionCall)
                    _returnValue += _functionCall.Function.Name + "("
                    For _counter As Integer = 0 To _functionCall.Parameters.Length - 2
                        Dim _functionCallParameter As Enhancements.Basic.Expressions.Token = _functionCall.Parameters(_counter)
                        _returnValue += __DetermineExecutionPart(_functionCallParameter) + ","
                    Next _counter
                    If _functionCall.Parameters.Length >= 1 Then
                        _returnValue += __DetermineExecutionPart(_functionCall.Parameters.Last())
                    End If
                    _returnValue += ")"
                End If
            End If
        End If

        Return _returnValue
    End Function

    Private Function __DetermineExecutionTime() As TimeSpan?
        Dim _returnValue As TimeSpan? = Nothing

        If __Time_CompileStart IsNot Nothing AndAlso __Time_SolveStop IsNot Nothing Then
            _returnValue = New TimeSpan(0, 0, 0, 0, 0)
            _returnValue = _returnValue.Value.Add(__Time_CompileStop.Value - __Time_CompileStart.Value)
            If __Time_CompileVariablesTypesRequestStart IsNot Nothing AndAlso __Time_CompileVariablesTypesRequestStop IsNot Nothing Then
                _returnValue = _returnValue.Value.Add(-(__Time_CompileVariablesTypesRequestStop.Value - __Time_CompileVariablesTypesRequestStart.Value))
            End If
            _returnValue = _returnValue.Value.Add(__Time_SolveStop.Value - __Time_SolveStart.Value)
        End If

        Return _returnValue
    End Function

End Class
